package main;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import functions.GeneralFunction;


public class Result {
	private List<ResultEntry> results;
	
	public Result() {
		this.results = new ArrayList<ResultEntry>();
	}
	
	private double calculateDelta(ContingencyTable t, GeneralFunction f,
			double value) {
		List<ContingencyTable> n = t.neighbors();
		double res = 0.0;
		if (!Double.isInfinite(value) && !Double.isNaN(value)) {
			int amount = 0;
			if (n.size() > 0) {
				for (ContingencyTable i : n) {
					double y = f.calculate(i);
					if (!Double.isInfinite(y) && !Double.isNaN(y)) {
						res += Math.abs(y - value);
						amount++;
					}
				}
				if (amount > 0)
					res = res / amount;
			}
		}
		return res;
	}
	
	// index in ContingencyTable:
	// 	0, 1;
	// 	2, 3
	//           in case when index < 0 arg gets abs value of index
	public void add(ContingencyTable t, GeneralFunction f, int index, double value) {
		double arg = 0;
		if (index == 0) arg = t.f11();
		else if (index == 1) arg = t.f10();
		else if (index == 2) arg = t.f01();
		else if (index == 3) arg = t.f00();
		else if (index < 0) arg = -1 * index;
		results.add(new ResultEntry(t, f, arg, value, calculateDelta(t, f, value)));
	}
	
	// V�ljundfaili �hes reas on tabulaatoritega eraldatult j�rgmised andmed:
	// f11	f10	f01	f00	FuncNimi	Argument	FuncV��rtus	Delta
	// * Argument - see on selle fxx v�lja v��rtus, mis kasvab l�igus [0,n] (et oleks lihtsam graafiku X-teljele v��rtust v�tta)
	// * Delta - see on 'naabertabelite keskmine kaugus'
	public void exportAll(String fileName) {
		try {
			FileWriter writer = new FileWriter(fileName);
			BufferedWriter out = new BufferedWriter(writer);
			for(ResultEntry re : results) {
				out.write(re.t + "\t" + re.f.getName() + "\t" + re.arg + "\t" + re.value + "\t" + re.delta + "\n");
			}
			out.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void exportAreaStats(String fileName) {
		try {
			List<AreaStats> statsList = new ArrayList<AreaStats>();
			
			for (ResultEntry re : results) {
				// otsime �ige objekti
				AreaStats stats = null;
				for (AreaStats s : statsList) {
					if (s.f.getName().equals(re.f.getName()) && s.area == (int)re.arg) {
						stats = s;
						break;
					}
				}
				if (stats == null) {
					stats = new AreaStats(re.f, (int)re.arg);
					statsList.add(stats);
				}
				stats.addValue(re.value, re.delta);
			}
			Collections.sort(statsList);
			FileWriter writer = null;
			BufferedWriter out = null;
			String fn = "";
			for (AreaStats s : statsList) {
				if (!s.f.getName().equals(fn)) {
					if (out != null) out.close();
					fn = s.f.getName();
					writer = new FileWriter(fn+"."+fileName);
					out = new BufferedWriter(writer);
				}
				if (s.f.getName().equals(fn)) {
					out.write(s.getValues() + "\n");
				}
			}
			if (out != null) out.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	@SuppressWarnings("rawtypes")
	private class AreaStats implements Comparable {
		private GeneralFunction f;
		private int area;
		private int count;
		private double min;
		private double max;
		private double sum;
		private double avg;
		private double deltaMin;
		private double deltaMax;
		private double deltaSum;
		private double deltaAvg;
		
		private AreaStats(GeneralFunction f, int area) {
			this.f = f;
			this.area = area;
			count = 0;
			min = deltaMin = Double.MAX_VALUE;
			max = deltaMax = Double.MIN_VALUE;
			avg = deltaAvg = sum = deltaSum = 0.0;
		}
		
		private void addValue(double value, double delta) {
			if (value < min && !Double.isNaN(value) && !Double.isInfinite(value)) min = value;
			if (value > max && !Double.isNaN(value) && !Double.isInfinite(value)) max = value;
			if (delta < deltaMin && !Double.isNaN(delta) && !Double.isInfinite(delta)) deltaMin = delta;
			if (delta > deltaMax && !Double.isNaN(delta) && !Double.isInfinite(delta)) deltaMax = delta;
			if (!Double.isNaN(value) && !Double.isNaN(delta) && !Double.isInfinite(value) && !Double.isInfinite(delta)) {
				count++;
				sum += value; avg = sum / count;
				deltaSum += delta; deltaAvg = deltaSum / count;
			}
		}
		
		private String getValues() {
			return f.getName() + "\t" + area + "\t" + min + "\t" +max + "\t" +avg + "\t" + deltaMin + "\t" +
			deltaMax + "\t" + deltaAvg  + "\t" + deltaAvg/avg;
		}

		@Override
		public int compareTo(Object o) {
			AreaStats x = (AreaStats)o;
			if (x.f.getName().compareTo(this.f.getName()) == 0 ) {
				if (x.area > this.area) return -1;
				else if (x.area < this.area) return 1;
				else return 0;
			} 
			else return x.f.getName().compareTo(this.f.getName())*-1;
		}
	}
	
	private class ResultEntry {
		private ContingencyTable t;
		private GeneralFunction f;
		private double arg;
		private double value;
		private double delta;
		
		private ResultEntry(ContingencyTable t, GeneralFunction f, double arg, double value, double delta) {
			this.t = t;
			this.f = f;
			this.arg = arg;
			this.value = value;
			this.delta = delta;
		}
	}

}
